#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QSerialPortInfo>
#include <QMessageBox>
#include <QFileDialog>
#include <QFileInfo>
#include <QString>

int serialPortStatus = 0;

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow), serialPort(new QSerialPort)
{
    ui->setupUi(this);

    // 在portBox中显示可用串口,并设置默认参数
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
        ui->comBox->addItem(info.portName());

    serialPort->setDataBits(QSerialPort::Data8);            // 设置数据位数
    serialPort->setParity(QSerialPort::NoParity);           // 设置校验位
    serialPort->setFlowControl(QSerialPort::NoFlowControl); // 设置流控制
    serialPort->setStopBits(QSerialPort::OneStop);          // 设置停止位

    ui->startTransmitBtn->setDisabled(true);
    ui->slctFileBtn->setDisabled(true);
}

MainWindow::~MainWindow()
{
    delete ui;
    delete serialPort;
}

void MainWindow::on_findComBtn_clicked()
{
    ui->comBox->clear();
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
        ui->comBox->addItem(info.portName());
}

void MainWindow::on_comSwitchBtn_clicked()
{
    static bool button_status = false;

    if (button_status == false)
    {
        serialPort->setPortName(ui->comBox->currentText());
        serialPort->setBaudRate(ui->baudrateBox->currentText().toInt());

        if (serialPort->open(QSerialPort::ReadWrite) == true)
        {
            button_status = true;

            ui->comBox->setDisabled(true);
            ui->baudrateBox->setDisabled(true);
            ui->slctFileBtn->setEnabled(true);
            ui->comSwitchBtn->setText("关闭串口");
            if (ui->filePath->text().isEmpty() != true)
            {
                ui->filePath->setEnabled(true);
            }
        }
        else
        {
            QMessageBox::warning(this, "串口打开失败,请检查串口是否已被占用！", "关闭");
        }
    }
    else
    {
        button_status = false;

        serialPort->close();

        ui->comBox->setEnabled(true);
        ui->baudrateBox->setEnabled(true);
        ui->comSwitchBtn->setText("连接串口");

        ui->slctFileBtn->setDisabled(true);
        ui->startTransmitBtn->setDisabled(true);
    }
}

void MainWindow::on_slctFileBtn_clicked()
{
    ui->filePath->setText(QFileDialog::getOpenFileName(this, "打开文件", ".", "Bin文件 (*.bin)"));
    if (ui->filePath->text().isEmpty() != true)
    {
        ui->startTransmitBtn->setEnabled(true);
    }
    else
    {
        ui->startTransmitBtn->setDisabled(true);
    }
}

QFile ubootFile;
QFileInfo ubootFileInfo;

void MainWindow::on_startTransmitBtn_clicked()
{
    QFile file(ui->filePath->text());               // 要读取的bin文件
    QFileInfo read_file_info(ui->filePath->text()); // 要读取的bin文件的信息
    if (!file.open(QIODevice::ReadOnly))
    { // 只读方式打开bin文件
        QMessageBox::warning(this, tr("Read File"),
                             tr("Cannot open file:\n%1").arg(ui->filePath->text()));
        return;
    }
    ui->startTransmitBtn->setDisabled(true);
    ui->slctFileBtn->setDisabled(true);
    QDataStream BinFileData(&file);                // 将刚刚创建的 file 对象的指针传递给一个QDataStream实例 BinFileData
    char *pBuff = new char[read_file_info.size()]; // 要读取bin文件的大小
    BinFileData.readRawData(pBuff, static_cast<int>(read_file_info.size()));

    const static int PACKET_SIZE = 1024;                     // 每次发送的字节数
    const static int PACKET_HEAD_SIZE = 14;                  // 协议字节数
    auto fileLen = read_file_info.size();                    // 待发送文件大小
    auto sendCnt = (int)ceil(fileLen / (PACKET_SIZE * 1.0)); // 发送次数
    auto lastLen = fileLen % PACKET_SIZE;                    // 最后一次发送的大小
    if (0 == lastLen)
    { // 恰巧是PACKET_SIZE的整数倍
        lastLen = PACKET_SIZE;
    }
    qDebug() << "fileLen,lastLen,sendCnt:" << fileLen << lastLen << sendCnt;
    for (int i = 0; i < sendCnt; i++)
    {
        if (i != sendCnt - 1)
        {
            const int PACKET_LEN = PACKET_HEAD_SIZE + PACKET_SIZE;
            char tx_buf[PACKET_LEN] = {0};
            tx_buf[0] = 0x42;                             // 帧头低字节
            tx_buf[1] = 0x59;                             // 帧头高字节
            tx_buf[2] = 0x01;                             // Flash ID
            tx_buf[3] = 0x32;                             // QSPI Flash指令
            tx_buf[4] = ((i * PACKET_SIZE) >> 16) & 0xFF; // 写入地址
            tx_buf[5] = ((i * PACKET_SIZE) >> 8) & 0xFF;  // 写入地址
            tx_buf[6] = (i * PACKET_SIZE) & 0xFF;         // 写入地址
            tx_buf[7] = PACKET_SIZE >> 8;                 // 数据段长度
            tx_buf[8] = PACKET_SIZE & 0xFF;               // 数据段长度
            memcpy(tx_buf + 9, pBuff + i * PACKET_SIZE, PACKET_SIZE);
            uint checksum = 0;
            for (int j = 0; j <= PACKET_SIZE; j++)
            {
                checksum += tx_buf[j]; // 数据段校验和
            }
            tx_buf[PACKET_LEN - 5] = (checksum >> 16) & 0xFF; // 写入地址
            tx_buf[PACKET_LEN - 4] = (checksum >> 8) & 0xFF;  // 写入地址
            tx_buf[PACKET_LEN - 3] = checksum & 0xFF;         // 写入地址
            tx_buf[PACKET_LEN - 2] = 0x4C;                    // 帧尾低字节
            tx_buf[PACKET_LEN - 1] = 0x48;                    // 帧尾高字节
            serialPort->write(QByteArray(tx_buf, PACKET_LEN));
            serialPort->waitForBytesWritten();
            // serialPort->flush();
        }
        else
        { // 最后一次发送
            int PACKET_LEN = PACKET_HEAD_SIZE + lastLen;
            char *tx_buf = new char[PACKET_LEN];
            memset(tx_buf, 0, PACKET_LEN);
            tx_buf[0] = 0x42;                             // 帧头低字节
            tx_buf[1] = 0x59;                             // 帧头高字节
            tx_buf[2] = 0x01;                             // Flash ID
            tx_buf[3] = 0x32;                             // QSPI Flash指令
            tx_buf[4] = ((i * PACKET_SIZE) >> 16) & 0xFF; // 写入地址
            tx_buf[5] = ((i * PACKET_SIZE) >> 8) & 0xFF;  // 写入地址
            tx_buf[6] = (i * PACKET_SIZE) & 0xFF;         // 写入地址
            tx_buf[7] = PACKET_SIZE >> 8;                 // 数据段长度
            tx_buf[8] = PACKET_SIZE & 0xFF;               // 数据段长度
            memcpy(tx_buf + 9, pBuff + i * PACKET_SIZE, PACKET_SIZE);
            uint checksum = 0;
            for (int j = 0; j <= PACKET_SIZE; j++)
            {
                checksum += tx_buf[j]; // 数据段校验和
            }
            tx_buf[PACKET_LEN - 5] = (checksum >> 16) & 0xFF; // 写入地址
            tx_buf[PACKET_LEN - 4] = (checksum >> 8) & 0xFF;  // 写入地址
            tx_buf[PACKET_LEN - 3] = checksum & 0xFF;         // 写入地址
            tx_buf[PACKET_LEN - 2] = 0x4C;                    // 帧尾低字节
            tx_buf[PACKET_LEN - 1] = 0x48;                    // 帧尾高字节
            serialPort->write(QByteArray(tx_buf, PACKET_LEN));
            serialPort->waitForBytesWritten();
            delete[] tx_buf;
        }
        float percentage = ((float)i * 100) / (float)sendCnt;
        ui->progressBar->setValue(percentage);
        serialPort->waitForReadyRead();
        QByteArray rcvBuff = serialPort->read(5);
        if (rcvBuff[0] == 0x42 && rcvBuff[1] == 0x59 && rcvBuff[3] == 0x4C && rcvBuff[4] == 0x48)
        {
            if (rcvBuff[2] == 0x01)
                continue; // 回应ACK信号，表示正常接收该数据
            else if (rcvBuff[2] == 0x55)
                --i; // 回应NACK信号，表示该数据已被接收，但校验失败，需要重新发送
            else
            {
                // nothing todo (理论无该状态)
            }
        }
        else
        {
            QMessageBox::warning(this, "错误", "非法回传信息");
            break;
        }
    }
    delete[] pBuff;
    file.close();
    ui->slctFileBtn->setEnabled(true);
    ui->startTransmitBtn->setEnabled(true);
}
